Skip to content

fix(android): fix Google Pay crash — Activity context and ActivityEventListener#49

Merged
RhysAtBolt merged 8 commits intomainfrom
fix/google-pay-activity-context
Apr 7, 2026
Merged

fix(android): fix Google Pay crash — Activity context and ActivityEventListener#49
RhysAtBolt merged 8 commits intomainfrom
fix/google-pay-activity-context

Conversation

@RhysAtBolt
Copy link
Copy Markdown
Contributor

@RhysAtBolt RhysAtBolt commented Apr 2, 2026

Description

Fix two bugs that caused Google Pay to crash and a third that sent the wrong auth header when fetching the Bolt APM config.

1. PaymentsClient created with wrong context (GooglePayModule.kt)
Wallet.getPaymentsClient() was called with reactApplicationContext (the Application context). The Google Pay SDK needs an Activity context to attach the payment sheet to a window — hence Tried to show an alert while not attached to an Activity.

2. onActivityResult never received (GooglePayModule.kt)
GooglePayModule never registered as an ActivityEventListener, so the result from the Google Pay sheet was silently swallowed and the pending Promise would hang forever. The module now implements ActivityEventListener and registers itself in init{} — no changes to MainActivity required.

3. Wrong header when fetching APM config (GoogleWallet.tsx)
The fetchGooglePayAPMConfig request used merchant_token as the header name instead of the correct x-publishable-key, causing the config fetch to fail before the payment sheet could even be shown.

Testing

  • Tap Google Pay button on Android — payment sheet opens without crash
  • Complete a Google Pay payment — onComplete callback fires with token + billing address
  • Cancel/dismiss the Google Pay sheet — onError callback fires with CANCELLED
  • Cold-start the app and tap Google Pay immediately — no NO_ACTIVITY rejection

Security Review

Important

A security review is required for every PR in this repository to comply with PCI requirements.

  • I have considered and reviewed security implications of this PR and included the summary below.

Security Impact Summary

No new data flows or secrets introduced. The x-publishable-key header fix corrects which key is sent to the Bolt APM config endpoint — the key itself was already present in the original code, just under the wrong header name. The Activity context change only affects where Google Pay's system UI is anchored; no payment data handling is altered.

…ng ActivityEventListener

- Pass Activity (not ApplicationContext) to Wallet.getPaymentsClient() so the
  payment sheet has a window to attach to, fixing the "not attached to an
  Activity" crash on button tap
- Implement ActivityEventListener and register it in init{} so onActivityResult
  is forwarded by the RN bridge — previously the pending Promise would hang
  forever as the result was never received
- Make handlePaymentResult private now that it is called internally via the
  listener rather than from MainActivity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@RhysAtBolt RhysAtBolt requested review from a team as code owners April 2, 2026 12:51
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Apr 2, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

RhysAtBolt and others added 2 commits April 2, 2026 13:54
Use the correct x-publishable-key header instead of merchant_token when
fetching the Google Pay APM config from Bolt's API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@SanthoshCharanBolt SanthoshCharanBolt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Added a comment

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Android Google Pay crash/hanging flows by ensuring the Google Pay SDK is invoked with an Activity context and by wiring onActivityResult delivery to the native module; also corrects the HTTP header used to fetch the Google Pay APM config on the JS side.

Changes:

  • Android: PaymentsClient is created with an Activity context and the module is registered as an ActivityEventListener to receive onActivityResult.
  • Android: payment-result handling is routed through the module’s onActivityResult and internalized (private).
  • JS: APM config fetch now sends x-publishable-key instead of merchant_token.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/payments/GoogleWallet.tsx Fixes the request header used to fetch Google Pay APM config.
android/src/main/java/com/boltreactnativesdk/GooglePayModule.kt Uses Activity context for Google Pay and registers for activity results to resolve/reject pending promises.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 44 to 48
const response = await fetch(`${apiUrl}/v1/apm_config/googlepay`, {
method: 'GET',
headers: {
merchant_token: publishableKey,
'x-publishable-key': publishableKey,
},
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change fixes the request header name used for the APM config fetch, but there’s no unit test asserting the request is sent with the expected header. Since this header is required for the config call to succeed, consider adding a Jest test that mocks fetch and verifies the x-publishable-key header is used.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added two tests to GoogleWallet.test.tsx that mock fetch and assert directly against fetchGooglePayAPMConfig: one verifying the correct header is passed, one verifying a non-ok response throws.

- Capitalize header to 'X-Publishable-Key' per convention (SanthoshCharanBolt)
- Add Bolt.apiHeaders() method to centralise the header for all API calls
  and use it in fetchGooglePayAPMConfig (SanthoshCharanBolt)
- Don't cache PaymentsClient — create per-call to avoid holding a stale
  Activity reference across rotation/recreation (Copilot)
- Override invalidate() to call removeActivityEventListener, preventing
  stale listeners on React instance teardown (Copilot)
- Clear pending fields before NO_ACTIVITY early return to avoid retaining
  references longer than necessary (Copilot)
- Add test for Bolt.apiHeaders() returning X-Publishable-Key (Copilot)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@SanthoshCharanBolt SanthoshCharanBolt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Added a comment

}
return paymentsClient!!
val walletOptions = Wallet.WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_TEST)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? We are setting env to test all the time?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed. Bolt.ts now exposes environment as a public field. buildNativeConfig maps it to googlePayEnvironment: 'PRODUCTION' | 'TEST' and passes it through the config JSON. GooglePayModule.kt has a new walletEnvFromConfig() helper that reads it and maps to WalletConstants.ENVIRONMENT_PRODUCTION / ENVIRONMENT_TEST — used by both isReadyToPay and requestPayment.

RhysAtBolt and others added 4 commits April 7, 2026 20:11
Replaces the hardcoded WalletConstants.ENVIRONMENT_TEST with a value
derived from the JS-side Bolt environment:
- production → ENVIRONMENT_PRODUCTION
- sandbox / staging → ENVIRONMENT_TEST

Changes:
- Bolt.ts: expose `environment` as a public readonly field
- GoogleWallet.tsx: pass `googlePayEnvironment` ("PRODUCTION"|"TEST")
  through buildNativeConfig so the native module can read it from configJson
- GooglePayModule.kt: walletEnvFromConfig() parses configJson and maps to
  WalletConstants; both isReadyToPay and requestPayment use it
- GoogleWallet.test.tsx: add fetchGooglePayAPMConfig tests verifying the
  correct header is sent and that non-ok responses throw; update useBolt
  mock to include apiHeaders/environment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The exhaustive-deps rule flagged bolt.environment being used inside
the isReadyToPay useEffect without being listed as a dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The react-native Jest preset resolves .ios.tsx before .tsx, so importing
from 'GoogleWallet' in tests loaded the iOS stub which only exports
GoogleWallet — not fetchGooglePayAPMConfig — causing 'is not a function'.

Moving the function to a platform-agnostic googlePayApi.ts lets the test
import it directly. GoogleWallet.tsx re-exports it to keep the public API
unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@RhysAtBolt RhysAtBolt merged commit d168760 into main Apr 7, 2026
7 checks passed
@RhysAtBolt RhysAtBolt deleted the fix/google-pay-activity-context branch April 7, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants